#include "stdafx.h"
#include "HXAlive.h"
#include "HXDirector.h"
#include "HXSpeech.h"

//=============================================================================

HXAliveProcessor g_alive;	// idle motion generator

//=============================================================================

//! Constructor.
HXAliveUnit::HXAliveUnit(unsigned int id)
: m_id(id),
  m_triggerIndex(0),
  m_triggerUse(false)
{
}

//! Destructor.
HXAliveUnit::~HXAliveUnit()
{
}

//! Start a motion when trigger has passed value (assumes prior Set() call).
void HXAliveUnit::TriggeredStart(unsigned int index)
{
	m_triggerUse = true;
	m_triggerIndex = index;
}

//! Does this unit trigger only once a particular word is reached?
inline
bool HXAliveUnit::IsTriggering()
{
	return m_triggerUse;
}

//! Set the expected trigger value.
inline
void HXAliveUnit::Trig(unsigned int index, bool andBelow)
{
	if (m_triggerUse)
	{
		if ((m_triggerIndex == index) || (andBelow && (m_triggerIndex < index)))
		{
			Start();
			m_triggerUse = false;
		}
	}
}

//-----------------------------------------------------------------------------

//! Constructor.
HXAliveCommand::HXAliveCommand(const wstring& command, bool async)
: HXAliveUnit(HXATF_COMMAND),
  m_command(command),
  m_bExecuted(false)
{
	if (async)
	{
		m_id |= HXATF_ASYNC;
	}
}

//! Execute the command.
void HXAliveCommand::Start()
{
	if (m_id & HXATF_ASYNC)
	{
		g_director.AsyncExecute(m_command.c_str());
	}
	else
	{
		g_director.WaitExecute(m_command.c_str());
	}
	m_bExecuted = true;
}

//! Has the command been executed?
inline
bool HXAliveCommand::IsFinished()
{
	return m_bExecuted;
}

//-----------------------------------------------------------------------------

//! Constructor
HXAliveChange::HXAliveChange(unsigned int id)
: HXAliveUnit(id),
  m_duration(0),
  m_delay(0),
  m_transformMode(0)
{
}

//! Set motion parameters (no starting).
void HXAliveChange::Set(float duration, float delay, HXBlendMode mode)
{
	m_duration = duration;
	m_delay = delay;
	m_transformMode = mode;
}

//! Increase motion parameters.
void HXAliveChange::Extend(float duration, float delay)
{
	m_duration += duration;
	m_delay += delay;
}

//! Start a motion now (assumes prior Set() call).
void HXAliveChange::Start()
{
	m_timer.startTimer();
}

//! Start a motion now.
void HXAliveChange::Start(float duration, float delay, HXBlendMode mode)
{
	Set(duration, delay, mode);
	Start();
}

//! Ignore delay and start now.
void HXAliveChange::SkipDelay()
{
	m_delay = GetElapsedTime();
}

//! Check whether motion has finished.
inline
bool HXAliveChange::IsFinished()
{
	return (GetElapsedTime() - m_delay) > m_duration;
}

//! Check time since motion start.
inline
float HXAliveChange::GetElapsedTime() 
{ 
	m_timer.stopTimer(); 
	return (float)m_timer.getElapsedTime();
}

//! Calculate required value change.
inline
float HXAliveChange::GetChange(float& max, float& progress)
{
	float time = GetElapsedTime() - m_delay;
	if (time > 0)
	{
		float change_absolute = max * Transform((time >= m_duration) ? 1.0f : time/m_duration);
		float change_relative = change_absolute - progress;
		progress = change_absolute;
		return change_relative;
	}
	return 0.0f;
}

//! Valid scaling functions.
inline
float HXAliveChange::Transform(float position)
{
	switch (m_transformMode)
	{
	case HXBM_LINEAR:
		return position;
	case HXBM_ROOT:
		return TransformRoot(position);
	case HXBM_IROOT:
		return TransformInvRoot(position);
	case HXBM_SQR:
		return TransformSqr(position);
	case HXBM_ISQR:
		return TransformInvSqr(position);
	case HXBM_HSIN:
		return TransformHalfSin(position);
	case HXBM_IHSIN:
		return TransformInvHalfSin(position);
	case HXBM_SIN:
		return TransformSin(position);
	case HXBM_SINC:
		return TransformSinc(position);
	case HXBM_MAX:
		return 1.0f;
	default:
		return TransformSin(position); // default is SIN for now
	}
}

//-----------------------------------------------------------------------------

//! Constructor
HXAliveMotion::HXAliveMotion(unsigned int id)
: HXAliveChange(id | HXATF_MOTION),
  m_morph_max(0),
  m_morph_progress(0),
  m_pGroup(NULL)
{
	if (!(m_id & HXATF_IDLE))
	{
		m_id |= HXATF_ACTIVE;
	}
}

//! Set motion parameter (morph change).
void HXAliveMotion::SetMorph(const wstring& morphTarget, float morph_max)
{
	m_morphTarget = morphTarget;
	m_morph_max = morph_max;
	m_id |= HXATF_MORPH;
}

//! Set motion parameter (rotation).
void HXAliveMotion::SetRotation(float x_max, float y_max, float z_max)
{
	if (x_max)
		m_rot_max[0] = x_max;
	if (y_max)
		m_rot_max[1] = y_max;
	if (z_max)
		m_rot_max[2] = z_max;
	m_id |= HXATF_ROTATION;
}

//! Set motion parameter (translation).
void HXAliveMotion::SetTranslation(float x_max, float y_max, float z_max)
{
	if (x_max)
		m_trans_max[0] = x_max;
	if (y_max)
		m_trans_max[1] = y_max;
	if (z_max)
		m_trans_max[2] = z_max;
	m_id |= HXATF_TRANSLATION;
}

//! Set motion parameter (translation).
void HXAliveMotion::SetScale(float x_max, float y_max, float z_max)
{
	if (x_max)
		m_scale_max[0] = x_max;
	if (y_max)
		m_scale_max[1] = y_max;
	if (z_max)
		m_scale_max[2] = z_max;
	m_id |= HXATF_SCALE;
}

//! Set 3D model group to operate on.
void HXAliveMotion::SetGroup(const wstring& groupName)
{
	m_pGroup = g_config.resourceGroup.GetDescendant(groupName);
}

//-----------------------------------------------------------------------------

//! Constructor
HXAliveProcessor::HXAliveProcessor()
: m_pDisplay(NULL),
  m_bIdleBehaviour(false)
{
	InitializeCriticalSectionAndSpinCount(&m_criticalSection_Alive, 0x80000400);
}

//! Destructor
HXAliveProcessor::~HXAliveProcessor()
{
	DeleteCriticalSection(&m_criticalSection_Alive);
}

//! Initialize processor.
void HXAliveProcessor::Run(HXDisplay* pDisplay)
{
	if (m_pDisplay = pDisplay)
	{
		m_pDisplay->SetAlive(this);
	}
}

//! Shut-down processor.
void HXAliveProcessor::Stop()
{
	Clear();
}

//! Add an HXAliveMotion into the processing queue.
void HXAliveProcessor::Add(HXAliveUnit* pCommand)
{
	EnterCriticalSection(&m_criticalSection_Alive);
	m_aliveQueue.push_back(pCommand);
	LeaveCriticalSection(&m_criticalSection_Alive);
}

//! Count the number of HXAliveMotions in the processing queue (of a particular HxAliveType).
size_t HXAliveProcessor::Count(HXAliveType id) 
{ 
	EnterCriticalSection(&m_criticalSection_Alive);
	list<HXAliveUnit*>::iterator pacit = m_aliveQueue.begin();
	size_t count = 0;
	while (pacit != m_aliveQueue.end())
	{
		if ((*pacit)->GetId() & id)
		{
			count++;
		}
		++pacit;
	}
	LeaveCriticalSection(&m_criticalSection_Alive);
	return count;
}

//! Count the number of HXAliveMotions in the processing queue (of a particular HxAliveType with custom identifier).
size_t HXAliveProcessor::Count(HXAliveType id, unsigned int subid) 
{ 
	EnterCriticalSection(&m_criticalSection_Alive);
	list<HXAliveUnit*>::iterator pacit = m_aliveQueue.begin();
	size_t count = 0;
	while (pacit != m_aliveQueue.end())
	{
		if ((*pacit)->GetId() & id)
		{
			if (((*pacit)->GetId() & HXAT_IDS) == subid)
			{
				count++;
			}
		}
		++pacit;
	}
	LeaveCriticalSection(&m_criticalSection_Alive);
	return count;
}

//! Remove commands and reset.
void HXAliveProcessor::Clear(unsigned int id, bool clearTriggeredOnly)
{
	EnterCriticalSection(&m_criticalSection_Alive);
	list<HXAliveUnit*>::iterator pacit = m_aliveQueue.begin();
	while (pacit != m_aliveQueue.end())
	{
		if ((*pacit)->GetId() & id)
		{
			if (!(*pacit)->IsTriggering())
			{
				if ((*pacit)->GetId() & HXATF_MOTION)
				{
					HXAliveMotion* pMotion = dynamic_cast<HXAliveMotion*>(*pacit);
					if (pMotion && m_pDisplay)
					{
						if (pMotion->GetId() & HXATF_MORPH)
						{
							m_pDisplay->ChangeMorphValue(((*pacit)->GetId() & HXATF_IDLE) ? HXMS_IDLE : HXMS_EMOTE, pMotion->GetMorphTarget(), pMotion->GetMorphProgress());
						}
						if (pMotion->GetId() & HXATF_ROTATION)
						{
							m_pDisplay->Rotate(*pMotion->GetGroup(), pMotion->GetRotXProgress(), pMotion->GetRotYProgress(), pMotion->GetRotZProgress());
						}
						if (pMotion->GetId() & HXATF_TRANSLATION)
						{
							m_pDisplay->Translate(*pMotion->GetGroup(), pMotion->GetTransXProgress(), pMotion->GetTransYProgress(), pMotion->GetTransZProgress());
						}
						if (pMotion->GetId() & HXATF_SCALE)
						{
							m_pDisplay->Scale(*pMotion->GetGroup(), pMotion->GetScaleXProgress(), pMotion->GetScaleYProgress(), pMotion->GetScaleZProgress());
						}
					}
				}
			}
			if (!clearTriggeredOnly || !(*pacit)->IsTriggering())
			{
				SAFE_DELETE(*pacit);
				pacit = m_aliveQueue.erase(pacit);
			}
			else
			{
				++pacit;
			}
		}
		else
		{
			++pacit;
		}
	}
	LeaveCriticalSection(&m_criticalSection_Alive);
}

//! Activate all delayed commands.
void HXAliveProcessor::SlowClear(unsigned int id)
{
	EnterCriticalSection(&m_criticalSection_Alive);
	list<HXAliveUnit*>::iterator pacit = m_aliveQueue.begin();
	while (pacit != m_aliveQueue.end())
	{
		if ((*pacit)->GetId() & id)
		{
			if (!(*pacit)->IsTriggering())
			{
				HXAliveChange* pChange = dynamic_cast<HXAliveChange*>(*pacit);
				if (pChange)
				{
					pChange->SkipDelay();
				}
			}
		}
		++pacit;
	}
	LeaveCriticalSection(&m_criticalSection_Alive);
}

//! Should be called whenever the 3D model gets drawn.
void HXAliveProcessor::Callback()
{
	if (m_bIdleBehaviour)
	{
		IdleGenerate();
	}
	EnterCriticalSection(&m_criticalSection_Alive);
	list<HXAliveUnit*>::iterator pacit = m_aliveQueue.begin();
	while (pacit != m_aliveQueue.end())
	{
		if ((*pacit)->IsTriggering())
		{
			++pacit;
		}
		else
		{
			if ((*pacit)->GetId() & HXATF_MOTION)
			{
				HXAliveMotion* pMotion = dynamic_cast<HXAliveMotion*>(*pacit);
				if (pMotion && m_pDisplay)
				{
					if (pMotion->GetId() & HXATF_MORPH)
					{
						m_pDisplay->ChangeMorphValue(((*pacit)->GetId() & HXATF_IDLE) ? HXMS_IDLE : HXMS_EMOTE, pMotion->GetMorphTarget(), pMotion->GetMorphChange());
					}
					if (pMotion->GetId() & HXATF_ROTATION)
					{
						m_pDisplay->Rotate(*pMotion->GetGroup(), pMotion->GetRotXChange(), pMotion->GetRotYChange(), pMotion->GetRotZChange());
					}
					if (pMotion->GetId() & HXATF_TRANSLATION)
					{
						m_pDisplay->Translate(*pMotion->GetGroup(), pMotion->GetTransXChange(), pMotion->GetTransYChange(), pMotion->GetTransZChange());
					}
					if (pMotion->GetId() & HXATF_SCALE)
					{
						m_pDisplay->Scale(*pMotion->GetGroup(), pMotion->GetScaleXChange(), pMotion->GetScaleYChange(), pMotion->GetScaleZChange());
					}
				}
			}
			if ((*pacit)->IsFinished())
			{
				SAFE_DELETE(*pacit);
				pacit = m_aliveQueue.erase(pacit);
			}
			else
			{
				++pacit;
			}
		}
	}
	LeaveCriticalSection(&m_criticalSection_Alive);
}

//! Starts any commands that are triggered by the passed value.
void HXAliveProcessor::Trig(unsigned int index, bool andBelow, unsigned int id)
{
	EnterCriticalSection(&m_criticalSection_Alive);
	list<HXAliveUnit*>::iterator pacit = m_aliveQueue.begin();
	while (pacit != m_aliveQueue.end())
	{
		if ((*pacit)->GetId() & id)
		{
			(*pacit)->Trig(index, andBelow);
			++pacit;
		}
	}
	LeaveCriticalSection(&m_criticalSection_Alive);
}

void HXAliveProcessor::IdleGenerate()
{
	for (size_t ixs = 0; ixs < g_config.idleExpressionSets.size(); ixs++)
	{
		unsigned int choice = 0;
		if (g_config.idleExpressionSets[ixs].size() > 1)
		{
			rand_s(&choice);
			choice %= g_config.idleExpressionSets[ixs].size();
		}
		HXIdleExpression& idleExpr = g_config.idleExpressionSets[ixs][choice];
		if (!Count(HXATF_MOTION, ixs+1))
		{
			unsigned int number;
			rand_s(&number);
			float intensity = (idleExpr.maxAmplitude - idleExpr.minAmplitude) * number / UINT_MAX + idleExpr.minAmplitude;
			rand_s(&number);
			float delay = (idleExpr.maxInterval - idleExpr.minInterval) * number / UINT_MAX + idleExpr.minInterval;
			rand_s(&number);
			float slowness = (idleExpr.maxDuration - idleExpr.minDuration) * number / UINT_MAX + idleExpr.minDuration;
			rand_s(&number);
			float hold = (idleExpr.maxHold - idleExpr.minHold) * number / UINT_MAX + idleExpr.minHold;
			for (size_t bsi = 0; bsi < idleExpr.expression.shapes.size(); bsi++)
			{
				HXAliveMotion* pCommand;
				pCommand = new HXAliveMotion((ixs+1) | HXATF_IDLE);
				pCommand->SetMorph(idleExpr.expression.shapes[bsi].name, intensity*idleExpr.expression.shapes[bsi].amplitude);
				pCommand->Start(slowness*idleExpr.expression.shapes[bsi].duration, delay+idleExpr.expression.shapes[bsi].offset, idleExpr.expression.shapes[bsi].rampFunc);
				Add(pCommand);
				pCommand = new HXAliveMotion((ixs+1) | HXATF_IDLE);
				pCommand->SetMorph(idleExpr.expression.shapes[bsi].name, -intensity*idleExpr.expression.shapes[bsi].amplitude);
				pCommand->Start(slowness*idleExpr.expression.shapes[bsi].duration, delay+idleExpr.expression.shapes[bsi].offset+(slowness+hold)*idleExpr.expression.shapes[bsi].duration, idleExpr.expression.shapes[bsi].rampFunc);
				Add(pCommand);
			}
			for (size_t mvi = 0; mvi < idleExpr.expression.movements.size(); mvi++)
			{
				HXAliveMotion* pCommand;
				pCommand = new HXAliveMotion((ixs+1) | HXATF_IDLE);
				if (idleExpr.expression.movements[mvi].mode == HXMOV_ROTATION)
					pCommand->SetRotation(idleExpr.expression.movements[mvi].x, idleExpr.expression.movements[mvi].y, idleExpr.expression.movements[mvi].z);
				else if (idleExpr.expression.movements[mvi].mode == HXMOV_TRANSLATION)
					pCommand->SetTranslation(idleExpr.expression.movements[mvi].x, idleExpr.expression.movements[mvi].y, idleExpr.expression.movements[mvi].z);
				else if (idleExpr.expression.movements[mvi].mode == HXMOV_SCALE)
					pCommand->SetScale(idleExpr.expression.movements[mvi].x, idleExpr.expression.movements[mvi].y, idleExpr.expression.movements[mvi].z);
				pCommand->SetGroup(idleExpr.expression.movements[mvi].group);
				pCommand->Start(slowness*idleExpr.expression.movements[mvi].duration, delay+idleExpr.expression.movements[mvi].offset, idleExpr.expression.movements[mvi].rampFunc);
				Add(pCommand);
				pCommand = new HXAliveMotion((ixs+1) | HXATF_IDLE);
				if (idleExpr.expression.movements[mvi].mode == HXMOV_ROTATION)
					pCommand->SetRotation(-idleExpr.expression.movements[mvi].x, -idleExpr.expression.movements[mvi].y, -idleExpr.expression.movements[mvi].z);
				else if (idleExpr.expression.movements[mvi].mode == HXMOV_TRANSLATION)
					pCommand->SetTranslation(-idleExpr.expression.movements[mvi].x, -idleExpr.expression.movements[mvi].y, -idleExpr.expression.movements[mvi].z);
				else if (idleExpr.expression.movements[mvi].mode == HXMOV_SCALE)
					pCommand->SetScale(-idleExpr.expression.movements[mvi].x, -idleExpr.expression.movements[mvi].y, -idleExpr.expression.movements[mvi].z);
				pCommand->SetGroup(idleExpr.expression.movements[mvi].group);
				pCommand->Start(slowness*idleExpr.expression.movements[mvi].duration, delay+idleExpr.expression.movements[mvi].offset+(slowness+hold)*idleExpr.expression.movements[mvi].duration, idleExpr.expression.movements[mvi].rampFunc);
				Add(pCommand);
			}
		}
	}
}

//-----------------------------------------------------------------------------